/******************************************************************************* * Copyright (c) 2012-2015 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.part.projectexplorer; import elemental.events.KeyboardEvent; import elemental.events.MouseEvent; import org.eclipse.che.api.project.shared.dto.ProjectDescriptor; import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.api.parts.base.BaseView; import org.eclipse.che.ide.api.project.tree.AbstractTreeNode; import org.eclipse.che.ide.api.project.tree.TreeNode; import org.eclipse.che.ide.collections.Array; import org.eclipse.che.ide.collections.Collections; import org.eclipse.che.ide.ui.tree.Tree; import org.eclipse.che.ide.ui.tree.TreeNodeElement; import org.eclipse.che.ide.util.input.SignalEvent; import com.google.gwt.core.client.Scheduler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.InlineLabel; import com.google.inject.Inject; import com.google.inject.Singleton; import org.vectomatic.dom.svg.ui.SVGImage; import javax.annotation.Nonnull; /** * Project Explorer view. * * @author Andrey Plotnikov * @author Artem Zatsarynnyy */ @Singleton public class ProjectExplorerViewImpl extends BaseView<ProjectExplorerView.ActionDelegate> implements ProjectExplorerView { protected Tree<TreeNode<?>> tree; private Resources resources; private FlowPanel projectHeader; private AbstractTreeNode<?> rootNode; private ProjectTreeNodeDataAdapter projectTreeNodeDataAdapter; /** Create view. */ @Inject public ProjectExplorerViewImpl(Resources resources, ProjectTreeNodeRenderer projectTreeNodeRenderer) { super(resources); this.resources = resources; projectTreeNodeDataAdapter = new ProjectTreeNodeDataAdapter(); tree = Tree.create(resources, projectTreeNodeDataAdapter, projectTreeNodeRenderer, true); setContentWidget(tree.asWidget()); projectHeader = new FlowPanel(); projectHeader.setStyleName(resources.partStackCss().idePartStackToolbarBottom()); tree.asWidget().ensureDebugId("projectExplorerTree-panel"); minimizeButton.ensureDebugId("projectExplorer-minimizeBut"); // create special 'invisible' root node that will contain 'visible' root nodes rootNode = new AbstractTreeNode<Void>(null, null, null, null) { @Nonnull @Override public String getId() { return "ROOT"; } @Nonnull @Override public String getDisplayName() { return "ROOT"; } @Override public boolean isLeaf() { return false; } @Override public void refreshChildren(AsyncCallback<TreeNode<?>> callback) { } }; tree.setTreeEventHandler(new Tree.Listener<TreeNode<?>>() { @Override public void onNodeAction(TreeNodeElement<TreeNode<?>> node) { delegate.onNodeAction(node.getData()); } @Override public void onNodeClosed(TreeNodeElement<TreeNode<?>> node) { } @Override public void onNodeContextMenu(final int mouseX, final int mouseY, TreeNodeElement<TreeNode<?>> node) { delegate.onNodeSelected(node.getData(), tree.getSelectionModel()); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { delegate.onContextMenu(mouseX, mouseY); } }); } @Override public void onNodeDragStart(TreeNodeElement<TreeNode<?>> node, MouseEvent event) { } @Override public void onNodeDragDrop(TreeNodeElement<TreeNode<?>> node, MouseEvent event) { } @Override public void onNodeExpanded(TreeNodeElement<TreeNode<?>> node) { delegate.onNodeExpanded(node.getData()); } @Override public void onNodeSelected(TreeNodeElement<TreeNode<?>> node, SignalEvent event) { delegate.onNodeSelected(node.getData(), tree.getSelectionModel()); } @Override public void onRootContextMenu(final int mouseX, final int mouseY) { Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { delegate.onContextMenu(mouseX, mouseY); } }); } @Override public void onRootDragDrop(MouseEvent event) { } @Override public void onKeyboard(KeyboardEvent event) { if (event.getKeyCode() == KeyboardEvent.KeyCode.ENTER) { delegate.onEnterKey(); } else if (event.getKeyCode() == KeyboardEvent.KeyCode.DELETE) { delegate.onDeleteKey(); } } }); } @Override public Array<TreeNode<?>> getOpenedTreeNodes() { Array<TreeNodeElement<TreeNode<?>>> treeNodes = tree.getVisibleTreeNodes(); Array<TreeNode<?>> openedNodes = Collections.createArray(); for (int i = 0; i < treeNodes.size(); i++) { TreeNodeElement<TreeNode<?>> treeNodeElement = treeNodes.get(i); if (treeNodeElement.isOpen()) { openedNodes.add(treeNodeElement.getData()); } } return openedNodes; } /** {@inheritDoc} */ @Override public void setRootNodes(@Nonnull final Array<TreeNode<?>> rootNodes) { // provided rootNodes should be set as child nodes for rootNode rootNode.setChildren(rootNodes); for (TreeNode<?> treeNode : rootNodes.asIterable()) { treeNode.setParent(rootNode); } tree.getSelectionModel().clearSelections(); tree.getModel().setRoot(rootNode); tree.renderTree(0); if (rootNodes.isEmpty()) { delegate.onNodeSelected(null, tree.getSelectionModel()); } else { final TreeNode<?> firstNode = rootNodes.get(0); if (!firstNode.isLeaf()) { // expand first node that usually represents project itself tree.autoExpandAndSelectNode(firstNode, false); delegate.onNodeExpanded(firstNode); } // auto-select first node tree.getSelectionModel().selectSingleNode(firstNode); delegate.onNodeSelected(firstNode, tree.getSelectionModel()); } } /** {@inheritDoc} */ @Override public void updateNode(@Nonnull TreeNode<?> oldNode, @Nonnull TreeNode<?> newNode) { // get currently selected node final Array<TreeNode<?>> selectedNodes = tree.getSelectionModel().getSelectedNodes(); TreeNode<?> selectedNode = null; if (!selectedNodes.isEmpty()) { selectedNode = selectedNodes.get(0); } Array<Array<String>> pathsToExpand = tree.replaceSubtree(oldNode, newNode, false); tree.expandPaths(pathsToExpand, false); // restore selected node if (selectedNode != null) { tree.getSelectionModel().selectSingleNode(selectedNode); } } /** {@inheritDoc} */ @Override public void selectNode(@Nonnull TreeNode<?> node) { tree.getSelectionModel().selectSingleNode(node); delegate.onNodeSelected(node, tree.getSelectionModel()); } /** {@inheritDoc} */ @Override public void expandAndSelectNode(@Nonnull TreeNode<?> node) { tree.autoExpandAndSelectNode(node, true); delegate.onNodeSelected(node, tree.getSelectionModel()); } /** {@inheritDoc} */ @Override public void setProjectHeader(@Nonnull ProjectDescriptor project) { if (toolBar.getWidgetIndex(projectHeader) < 0) { toolBar.addSouth(projectHeader, 28); setToolbarHeight(50); } projectHeader.clear(); FlowPanel delimiter = new FlowPanel(); delimiter.setStyleName(resources.partStackCss().idePartStackToolbarSeparator()); projectHeader.add(delimiter); SVGImage projectVisibilityImage = new SVGImage("private".equals(project.getVisibility()) ? resources.privateProject() : resources.publicProject()); projectVisibilityImage.getElement().setAttribute("class", resources.partStackCss().idePartStackToolbarBottomIcon()); projectHeader.add(projectVisibilityImage); InlineLabel projectTitle = new InlineLabel(project.getName()); projectHeader.add(projectTitle); } /** {@inheritDoc} */ @Override public void hideProjectHeader() { toolBar.remove(projectHeader); setToolbarHeight(22); } @Nonnull @Override public TreeNode<?> getSelectedNode() { // Tree always must to have one selected node at least. // Return the first one until we don't support multi-selection. return tree.getSelectionModel().getSelectedNodes().get(0); } @Override protected void focusView() { tree.asWidget().getElement().getFirstChildElement().focus(); } }